package Gee

import (
	"html/template"
	"log"
	"net/http"
	"strings"
	"time"
)

// HandlerFunc 处理函数
//type HandlerFunc func(http.ResponseWriter, *http.Request)
type HandlerFunc func(*Context)

// Engine 实现接口（ServeHTTP）
type Engine struct {
	*RouterGroup // 继承路由组 可以注册全局中间件
	router       *router
	groups       []*RouterGroup // 所有分组

	// html模板渲染
	htmlTemplates *template.Template // 模板
	funcMap       template.FuncMap   // map
}

// RouterGroup 实现路由分组
type RouterGroup struct {
	prefix      string        // 分组前缀
	middlewares []HandlerFunc // 对于该分组使用的中间件
	parent      *RouterGroup  // 父分组
	engine      *Engine       // 用时实现中间件调用
}

// 初始化engine实例
func New() *Engine {
	engine := &Engine{router: newRouter()}
	engine.RouterGroup = &RouterGroup{engine: engine}
	engine.groups = []*RouterGroup{engine.RouterGroup}
	return engine
}
func Default() *Engine {
	engine := New()
	engine.Use(Logger(), Recovery())
	return engine
}

// Group 创建分组对象
func (group *RouterGroup) Group(prefix string) *RouterGroup {
	engine := group.engine
	newGroup := &RouterGroup{
		prefix: group.prefix + prefix,
		parent: group,
		engine: engine,
	}
	engine.groups = append(engine.groups, newGroup)
	return newGroup
}

// 注册中间件
func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
	group.middlewares = append(group.middlewares, middlewares...)
}
func (group *RouterGroup) addRouter(method, comp string, handler HandlerFunc) {
	engine := group.engine
	engine.router.addRoute(method, group.prefix+comp, handler)
}

// 路由组外部接口
func (group *RouterGroup) GET(pattern string, handler HandlerFunc) {
	group.addRouter("GET", pattern, handler)
}
func (group *RouterGroup) POST(pattern string, handler HandlerFunc) {
	group.addRouter("POST", pattern, handler)
}

// 获取服务器静态文件资源
// relativePath 相对路径
// root 根路径
func (group *RouterGroup) Static(relativePath string, root string) {
	handler := group.createStaticHandler(relativePath, http.Dir(root))
	urlPattern := relativePath + "/*filepath"
	group.GET(urlPattern, handler)
}

//例如 请求路由为 /assets/js/test.js 根路径为 /usr
// /assets 为相对路径的路由前缀；js/test.js：为相对路径； /usr 为绝对路径根目录；实现了 /assets/js/test.js ==> /usr/js/test.js 的转换
func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileSystem) HandlerFunc {
	absolutePath := group.prefix + relativePath                       // 路由组前缀 和 相对路径  ==> 相对路径A group : /assets ;
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs)) // 使用绝对路径 替换相对路径
	return func(c *Context) {
		file := c.Param("filepath")
		_, err := fs.Open(file) // open 绝对路径下的 路由中相对路径中的filepath
		if err != nil {
			c.Status(http.StatusNotFound)
			return
		}
		fileServer.ServeHTTP(c.Writer, c.Req)
	}

}

// engine 处理请求
func (engine *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	c := newContext(w, r)
	c.engine = engine
	// 取出路由组中注册的中间件 需要满足的条件：请求的path 以路由组中的前缀相同
	routerPath := r.URL.Path
	for _, group := range engine.groups {
		if strings.HasPrefix(routerPath, group.prefix) {
			c.handlers = append(c.handlers, group.middlewares...)
		}
	}
	engine.router.handle(c)
}

// 路由实例外部接口
func (engine *Engine) GET(pattern string, handler HandlerFunc) {
	engine.addRoute("GET", pattern, handler)
}
func (engine *Engine) POST(pattern string, handler HandlerFunc) {
	engine.addRoute("POST", pattern, handler)
}

// 调用router中的函数构建路由前缀树，并注册路由映射到handler
func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) {
	engine.router.addRoute(method, pattern, handler)
}

//html 模板渲染
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
	engine.funcMap = funcMap
}
func (engine *Engine) LoadHTMLGlob(pattern string) {
	engine.htmlTemplates = template.Must(template.New("").Funcs(engine.funcMap).ParseGlob(pattern))
}

// 中间件 日志
func Logger() HandlerFunc {
	return func(c *Context) {
		start := time.Now()
		c.Next()
		log.Printf("[%d] \"%s\" in %v", c.StatusCode, c.Req.RequestURI, time.Since(start))
	}
}

// 启动服务
func (engine *Engine) Run(addr string) (err error) {
	return http.ListenAndServe(addr, engine)
}
